#pragma once

#include "google/protobuf/stubs/callback.h"
#include "netlib/net/tcp_server.h"
#include "netlib/rpc/protobuf_codec.h"

namespace google::protobuf {
class Service;
} // namespace google::protobuf

namespace netlib::rpc {

class RpcMessage;
using RpcCodec = ProtobufCodec<RpcMessage>;

class RpcServer : NonCopyable {
public:
	RpcServer(net::EventLoop* loop,
	          const net::InetAddress& listen_addr,
	          bool is_epoll = false,
	          std::string&& name = "RpcServer",
	          int n_event_thread = 0);

	void Start() { server_.Start(); }
	void Register(::google::protobuf::Service* service);

private:
	void OnMessage(const net::TcpConnectionPtr& conn, net::Buffer* buf, Timestamp ts);
	void OnRpcMessage(const net::TcpConnectionPtr& conn,
	                  std::unique_ptr<ProtobufMessage>&& msg,
	                  Timestamp ts);

private:
	net::TcpServer server_;
	std::unordered_map<std::string, ::google::protobuf::Service*> services_;
	ProtobufCodec<RpcMessage> codec_;
};

class RpcClosure : public ::google::protobuf::Closure {
public:
	RpcClosure(const net::TcpConnectionPtr& conn,
	           ProtobufMessage* response,
	           int64_t id,
	           const ProtobufCodec<RpcMessage>& codec) :
	    conn_(conn),
	    response_(response), id_(id), codec_(codec) {}

	void Run() override;

private:
	const net::TcpConnectionPtr& conn_;
	std::unique_ptr<ProtobufMessage> response_;
	int64_t id_;
	const ProtobufCodec<RpcMessage>& codec_;
};

} // namespace netlib::rpc